package org.xx.armory.web.tags;

import org.apache.commons.io.IOUtils;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.TagSupport;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;

abstract class BaseTagHandler extends TagSupport {
    private static final String RENDERER_STACK_KEY = "__jsp_renderer_stack__";
    private static final long serialVersionUID = 1L;

    protected final String getStringValue(String key) {
        return Objects.toString(this.getValue(key), "");
    }

    final InputStream getStaticResourceAsStream(String path) {
        Objects.requireNonNull(path, "path cannot be null");
        path = "/web/" + path;
        InputStream ret = this.pageContext.getServletContext().getResourceAsStream(path);
        return ret != null ? ret : new ByteArrayInputStream(new byte[0]);
    }

    public int doEndTag()
            throws JspException {
        TagUtils.addId(this.pageContext, this.getId());
        return 6;
    }

    protected final void pushRenderer() {
        this.getRendererStack().push(new JspRenderer(this.pageContext.pushBody()));
    }

    protected final String popRenderer() {
        JspRenderer renderer = this.getRendererStack().pop();
        JspWriter pw = renderer.getWriter();
        IOUtils.closeQuietly(renderer);
        this.pageContext.popBody();
        return pw instanceof BodyContent ? ((BodyContent) pw).getString() : "";
    }

    final JspRenderer getRenderer() {
        return this.getRendererStack().peek();
    }

    private BaseTagHandler.RendererStack getRendererStack() {
        BaseTagHandler.RendererStack rendererStack = (BaseTagHandler.RendererStack) this.pageContext.getAttribute(RENDERER_STACK_KEY);
        if (rendererStack == null) {
            rendererStack = new BaseTagHandler.RendererStack();
            rendererStack.push(new JspRenderer(this.pageContext.getOut()));
            this.pageContext.setAttribute(RENDERER_STACK_KEY, rendererStack);
        }

        return rendererStack;
    }

    void setAllAttributes() {
        if (this.getId() != null) {
            this.getRenderer().setAttribute("id", this.getId());
        }

        final Enumeration<?> keys = this.getValues();

        while (keys != null && keys.hasMoreElements()) {
            String key = (String) keys.nextElement();
            Object value = this.getValue(key);
            if (value != null) {
                this.getRenderer().setAttribute(key, value.toString());
            }
        }

    }

    private static final class RendererStack {
        private final List<JspRenderer> data;

        private RendererStack() {
            this.data = new ArrayList<>();
        }

        final JspRenderer pop() {
            int li = this.data.size() - 1;
            if (li < 0) {
                throw new IllegalStateException("empty stack");
            } else {
                return this.data.remove(li);
            }
        }

        final JspRenderer peek() {
            int li = this.data.size() - 1;
            if (li < 0) {
                throw new IllegalStateException("empty stack");
            } else {
                return this.data.get(li);
            }
        }

        final void push(JspRenderer renderer) {
            this.data.add(Objects.requireNonNull(renderer, "renderer cannot be null"));
        }
    }
}

